#include "world.h"
#include "Item.h"
#include "tile.h"
#include "Creature.h"
#include <fstream>
#include <iostream>
#include <assert.h>

using std::ifstream;
using std::cerr;
using std::endl;

#define min(a, b) ((a) > (b) ? (b) : (a))

struct BYTE_TO_POS
{
    char   m_byte;
    ushort m_tid;
    size_t m_color;
};

BYTE_TO_POS BTP[] =
{
#include "mapInfo"
};


namespace test
{
    MapBase *g_map = NULL;
};

BackGroudMap::BackGroudMap()
{
    m_mapbmp = create_bitmap(SCREEN_W, SCREEN_H);
    m_minimap = create_bitmap(MAP_W, MAP_H);
    assert(m_mapbmp);
    assert(m_minimap);
}

BackGroudMap::~BackGroudMap()
{
    destroy_bitmap(m_mapbmp);
    destroy_bitmap(m_minimap);
    cerr << __PRETTY_FUNCTION__ << endl;
}

bool BackGroudMap::loadMap(const char *filename)
{
    ifstream infile(filename);
    if (!infile)
        return false;

    uint line = 0;
    uint col = 0;

    char buf[512];
    while (infile.getline(buf, sizeof(buf)) && line < MAP_H)
    {
        for (col = 0; buf[col] && col < MAP_W; ++col)
        {
            for (uint k = 0; k < sizeof(BTP)/sizeof(BTP[0]); ++k)
            {
                if (buf[col] == BTP[k].m_byte)
                {
                    m_map[line][col].m_off = k;
                    m_map[line][col].m_tile = INST(TileManager).getTile(BTP[k].m_tid);
                    m_map[line][col].m_mini_color = BTP[k].m_color;
                    break;
                }
            }
        }
        ++line;
    }

    return true;
}

bool BackGroudMap::isValidPoint(const Rect &rect)
{
    return rect.x/64 < MAP_W && rect.y/64 < MAP_H;
}

void BackGroudMap::draw(BITMAP *surface)
{
    clear_to_color(m_mapbmp, makecol(255, 0, 255));

    short sx, sy;
    short x = INST(Config).cur_x - 8;
    short y = INST(Config).cur_y - 8;
    short nx = min(x+32, MAP_W);
    short ny = min(y+32, MAP_H);
    x = (x < 0 ? 0 : x);
    for (; x < nx; ++x)
    {
        y = INST(Config).cur_y - 8;
        y = (y < 0 ? 0 : y);
        for (; y < ny; ++y)
        {
            mapxy_to_screenxy(x*64, y*64, sx, sy);
            if (sx < -64 || sy < -64 || sx > SCREEN_W || sy > SCREEN_H)
                continue;
            if (m_map[y][x].m_tile)
            {
                draw_sprite(m_mapbmp, m_map[y][x].m_tile, sx, sy);
                putpixel(m_minimap, x, y, m_map[y][x].m_mini_color);
            }
        }
    }

    if (need_blit())
    {
        masked_blit(m_mapbmp, surface, 0, 0, 0, 0, m_mapbmp->w, m_mapbmp->h);
    }
}

void BackGroudMap::drawMiniMap(BITMAP *surface)
{
    short x, y;
    clear_to_color(m_minimap, makecol(255, 0, 255));

    for (x = 0; x < MAP_W; ++x)
    {
        for (y = 0; y < MAP_H; ++y)
        {
            if (m_map[y][x].m_tile)
                putpixel(m_minimap, x, y, m_map[y][x].m_mini_color);
        }
    }

    if (need_blit())
        masked_blit(m_minimap, surface, 0, 0, surface->w - m_minimap->w, 0, m_minimap->w, m_minimap->h);
}

MonsterMap::MonsterMap()
{
    memset(m_map, 0, sizeof(m_map));
}

MonsterMap::~MonsterMap()
{
    clean();
    cerr << __PRETTY_FUNCTION__ << endl;
}

bool MonsterMap::loadMap(const char *filename)
{
    return true;
}

bool MonsterMap::isValidPoint(const Rect &rect)
{
     short x = rect.x/64;
     short y = rect.y/64;
     if (x < MAP_W&& y < MAP_H)
        return m_map[y][x] == NULL;
    else
        return false;
}

Monster *MonsterMap::get(short x, short y)
{
    return m_map[y][x];
}

bool MonsterMap::put(Monster *monster, short x, short y)
{
    if (m_map[y][x])
        return false;
    m_map[y][x] = monster;
    monster->used();
    monster->getPosition() = Rect(x*64, y*64, 20, 20);
    return true;
}

void MonsterMap::remove(short x, short y)
{
    if (m_map[y][x])
    {
        m_map[y][x]->unused();
        m_map[y][x] = NULL;
    }
}

void MonsterMap::clean()
{
    for (short y = 0; y < MAP_H; ++y)
    {
        for (short x = 0; x < MAP_W; ++x)
        {
            remove(x, y);
        }
    }
}

void MonsterMap::draw(BITMAP *surface)
{
    BITMAP *mapbmp = getmapbmp();
    if (!mapbmp) return;

    getbg()->draw(surface);

    short sx, sy;
    short x = INST(Config).cur_x - 8;
    short y = INST(Config).cur_y - 8;
    short nx = min(x+32, MAP_W);
    short ny = min(y+32, MAP_H);
    x = (x < 0 ? 0 : x);
    for (; x < nx; ++x)
    {
        y = INST(Config).cur_y - 8;
        y = (y < 0 ? 0 : y);
        for (; y < ny; ++y)
        {
            mapxy_to_screenxy(x*64, y*64, sx, sy);
            if (sx < -64 || sy < -64 || sx > SCREEN_W || sy > SCREEN_H)
                continue;

            if (m_map[y][x]) m_map[y][x]->draw(mapbmp);
        }
    }

    INST(Player).draw(mapbmp);
    if (need_blit())
    {
        masked_blit(mapbmp, surface, 0, 0, 0, 0, mapbmp->w, mapbmp->h);
    }
}

void MonsterMap::drawMiniMap(BITMAP *surface)
{
    BITMAP *minimap = getminimapbmp();
    if (!minimap) return;

    getbg()->drawMiniMap(surface);

    short x, y;
    for (x = 0; x < MAP_W; ++x)
        for (y = 0; y < MAP_H; ++y)
        {
            if (m_map[y][x])
            {
                circlefill(minimap, x, y, 2, makecol(255, 0, 0));
            }
        }

    circlefill(minimap, INST(Config).cur_x+8, INST(Config).cur_y+8, 2, makecol(0, 255, 0));
    if (need_blit())
        masked_blit(minimap, surface, 0, 0, surface->w - minimap->w, 0, minimap->w, minimap->h);
}


ItemMap::ItemMap()
{
    memset(m_map, 0, sizeof(m_map));
}

ItemMap::~ItemMap()
{
    clean();
    cerr << __PRETTY_FUNCTION__ << endl;
}

bool ItemMap::loadMap(const char *filename)
{
    return true;
}

bool ItemMap::isValidPoint(const Rect &rect)
{
    return true;
}

void ItemMap::draw(BITMAP *surface)
{
    BITMAP *mapbmp = getmapbmp();
    if (!mapbmp) return;

    getbg()->draw(surface);

    short sx, sy;
    short x = INST(Config).cur_x - 8;
    short y = INST(Config).cur_y - 8;
    short nx = min(x+32, MAP_W);
    short ny = min(y+32, MAP_H);
    x = (x < 0 ? 0 : x);
    for (; x < nx; ++x)
    {
        y = INST(Config).cur_y - 8;
        y = (y < 0 ? 0 : y);
        for (; y < ny; ++y)
        {
            mapxy_to_screenxy(x*64, y*64, sx, sy);
            if (sx < -64 || sy < -64 || sx > SCREEN_W || sy > SCREEN_H)
                continue;

            for (short k = 0; k < ITEM_MAX; ++k)
            {
                if (m_map[y][x][k])
                {
                    m_map[y][x][k]->draw(mapbmp, sx+16*k, sy);
                }
            }
        }
    }

    if (need_blit())
    {
        masked_blit(mapbmp, surface, 0, 0, 0, 0, mapbmp->w, mapbmp->h);
    }
}

void ItemMap::drawMiniMap(BITMAP *surface)
{
    BITMAP *minimap = getminimapbmp();
    if (!minimap) return;

    getbg()->drawMiniMap(surface);

    if (need_blit())
        masked_blit(minimap, surface, 0, 0, surface->w - minimap->w, 0, minimap->w, minimap->h);
}


Item *ItemMap::get(short x, short y, short n)
{
    if (n >= ITEM_MAX || n < 0)
        return NULL;
    return m_map[y][x][n];
}

bool ItemMap::put(Item *item, short x, short y)
{
    if (x >= MAP_W || y >= MAP_H)
        return false;

    size_t i;
    int tmpx, tmpy;
    int tryx[] = {0, 0,  0, 1, 1, 1, -1, -1, -1};
    int tryy[] = {0, -1, 1, 0, -1, 1, 0, -1, 1};
    for (i = 0; i < sizeof(tryx)/sizeof(tryx[0]); ++i)
    {
        tmpy = tryy[i] + (int)y;
        tmpx = tryx[i] + (int)x;
        if (tmpy >= MAP_H || tmpx >= MAP_W || tmpy < 0 || tmpx < 0)
            continue;

        if (_put(item, tmpx, tmpy))
            return true;
    }

    return false;
}

bool ItemMap::_put(Item *item, short x, short y)
{
    for (short k = 0; k < ITEM_MAX; ++k)
    {
        if (!m_map[y][x][k])
        {
            m_map[y][x][k] = item;
            item->setMapPos(x, y);
            return true;
        }
    }
    return false;
}

bool ItemMap::remove(short x, short y, short n)
{
    if (n < 0) return false;
    if (n < ITEM_MAX)
    {
        if (m_map[y][x][n])
        {
            m_map[y][x][n] = NULL;
            return true;
        }
        else
        {
            return false;
        }
    }

    for (short k = 0; k < ITEM_MAX; ++k)
    {
        m_map[y][x][k] = NULL;
    }

    return true;
}

bool ItemMap::remove(short x, short y, Item *item)
{
    for (short k = 0; k < ITEM_MAX; ++k)
    {
        if (m_map[y][x][k] == item)
        {
            m_map[y][x][k] = NULL;
            return true;
        }
    }
    return false;
}

void ItemMap::clean()
{
    for (short y = 0; y < MAP_H; ++y)
    {
        for (short x = 0; x < MAP_W; ++x)
        {
            remove(x, y, ITEM_MAX);
        }
    }
}

bool MapVisitor::put_monster(Monster *m, MapBase *mb, short x, short y)
{
    while (mb)
    {
        MonsterMap * mm = dynamic_cast<MonsterMap *>(mb);
        if (mm)
        {
            return mm->put(m, x, y);
        }
        else
        {
            mb = mb->getbg();
        }
    }

    return false;
}

bool MapVisitor::put_item(Item *m, MapBase *mb, short x, short y)
{
    while (mb)
    {
        ItemMap * mm = dynamic_cast<ItemMap *>(mb);
        if (mm)
        {
            return mm->put(m, x, y);
        }
        else
        {
            mb = mb->getbg();
        }
    }

    return false;
}
